/* Joey Bachand
11/25/2013
Producer Consumers Program */
//This is fun!
//This is really fun!
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
//another comment111111hello
sem_t empty[2], full[2];
pthread_mutex_t mutex[2];
//another comment butter!
#define SIZE 5
#define FACTOR 0.000000001
int buffer[SIZE];
void insertItem(int *item, int n_buf);
void removeItem(int *item, int n_buf);
void *producer(void *prod_Id);
void *consumer(void *cons_Id);
static void usage(void);
static void sigHandler(int sig);

struct buffer {
	int item;
	struct buffer *next;
}*temp, *p_node[2], *c_node[2], *buff[2];

struct threads
{
	pthread_t id;
	long thread_id;
	struct threads *next;
}*p, *c, *p_head, *c_head;



int main(int argc, char *argv[])
{ 

int i, j;
int sleepTime, numProducers, numConsumers;
pthread_t prodId, consId;
pthread_attr_t attr;


//1. Get command line arguments argc, argv[1],argv[2],argv[3]
//2. Check for errors in the arguments;
//if any errors, output appropriate error message and exit.
//3. Assign argv[1], argv[2] and argv[3] to sleepTime, numProducers,
//and numConsumers, respectively.

if(argc != 4) usage();

sleepTime = atoi(argv[1]);
if(sleepTime < 1){
	printf("%s\n", "Sleeptime must be a positive integer!");
	usage();
}

numProducers = atoi(argv[2]);
if(numProducers < 1){
	printf("%s\n", "The number of producers must be a positive integer!");
	usage();
}

numConsumers = atoi(argv[3]);
if(numConsumers < 1){
	printf("%s\n", "The number of consumers must be a positive integer!");
	usage();
}



//4. Initialize buffer entries with -1


buff[0] = (struct buffer *) malloc( sizeof(struct buffer));
buff[1] = (struct buffer *) malloc( sizeof(struct buffer));

for(j=0; j<2; j++){
	temp = buff[j];
	for(i=0; i<SIZE; i++){
		temp->next = (struct buffer *) malloc( sizeof(struct buffer));
		temp->item = -1;
		temp = temp->next;
	}
	temp->next = buff[j];
}


p_node[0] = buff[0];
p_node[1] = buff[1];
c_node[0] = buff[0];
c_node[1] = buff[1];

//5. Create and initialize full, empty and mutex

sem_init(&empty[0], 0, SIZE);
sem_init(&full[0], 0, 0);
pthread_mutex_init(&mutex[0], NULL);
sem_init(&empty[1], 0, SIZE);
sem_init(&full[1], 0, 0);
pthread_mutex_init(&mutex[1], NULL);



//6. Create numProducers producer thread(s)

pthread_attr_init(&attr);


int index = 0;
for(index=0; index<numProducers; index++){
	p = (struct threads *) malloc( sizeof(struct threads));
	if(index == 0) p_head = p;
	p->thread_id =index;
	printf("thread_id %ld\n",p->thread_id );
	pthread_create(&(p->id), &attr, producer, (void *)(p->thread_id));
	p=p->next;
}


//7. Create numConsumers consumer thread(s)
for(index=0; index<numConsumers; index++){
	c = (struct threads *) malloc( sizeof(struct threads));
	if(index == 0) c_head = c;
	c->thread_id =index;
	pthread_create(&(c->id), &attr, consumer, (void *)(c->thread_id));
	c=c->next;
}

//8. Sleep for sleepTime
sleep(sleepTime);

while ((pthread_kill(p_head->id, SIGUSR1) == 0) && p_head->next != NULL) p_head=p_head->next;
while ((pthread_kill(c_head->id, SIGUSR1) == 0) && c_head->next != NULL) c_head=c_head->next; 

return 0;
}

void *producer(void *prod_Id){
	pthread_t pid = (pthread_t) prod_Id;
	int item;
	int n_buf;
	int x;
	int i=0;

	if  (signal(SIGUSR1, sigHandler) == SIG_ERR)
		exit(EXIT_FAILURE);
	
	while(1){
		x = rand()*FACTOR;
		sleep(x);
		n_buf = rand() % 2;
		item = rand();
		sem_wait(&empty[n_buf]);
		insertItem(&item, n_buf);
		sem_post(&full[n_buf]);
		printf(" producer %ld inserted %d into buffer %d\n", pid, item, n_buf);
	}
	pthread_exit(0);
}

void *consumer(void *cons_Id){
	pthread_t cid = (pthread_t) cons_Id;
	int item;
	int n_buf;
	int x;
	int i;

	if  (signal(SIGUSR1, sigHandler) == SIG_ERR)
		exit(EXIT_FAILURE);
	
	while(1){
		x = rand()*FACTOR;
		sleep(x);
		n_buf = rand()%2;
		sem_wait(&full[n_buf]);
		removeItem(&item, n_buf);
		sem_post(&empty[n_buf]);
		printf(" consumer %ld removed  %d from buffer %d\n", cid, *(&item), n_buf);

	}
	pthread_exit(0);
}

void insertItem(int *item, int n_buf){
	pthread_mutex_lock(&mutex[n_buf]);
	(p_node[n_buf])->item = *item;
	(p_node[n_buf]) = (p_node[n_buf])->next;
	pthread_mutex_unlock(&mutex[n_buf]);
}

void removeItem(int *item, int n_buf){
	pthread_mutex_lock(&mutex[n_buf]);
	*item = (c_node[n_buf])->item;
	(c_node[n_buf])->item = -1;
	(c_node[n_buf]) = (c_node[n_buf])->next;
	pthread_mutex_unlock(&mutex[n_buf]);
}

static void usage(void){
		(void)fprintf(stderr,
	    "usage: a.out [Sleep Time in seconds] [number of producers] [number of consumers]\n");
	exit(1);
}

static void sigHandler(int sig){

	if(sig == SIGUSR1) {
		pthread_exit(0);
		return;
	}

	return;
}
